API Gateway の API Key を調べてみた
西田@大阪です
API Gateway の API Keyと使用量プランを使ってクォータ、スロットリングでAPI制御する方法を調べてみました
何に使えるの?
ユーザーが一定期間内に何回APIを利用したかを計測し、計測した値をもとにクォータやスロットリングなどの制限を設定することができます
ただしAPI Keyを唯一の認証にはつかってはいけません。API Key は API に対してではなく、利用量プランに紐づくため、利用量プランが別のAPIに紐付けられると、同じAPI Keyを使って利用可能となってしまいます
そのため、思わぬユーザーにAPIをアクセスされる可能性があります
検証方法
API Keyを検証する方法は以下の2種類あります
- Custom Authorizer
- HTTP HEADER
使用量プランの制限
利用料プランを使ってユーザーのAPI利用を制限するには「クォータ」と「スロットリング」を使用します
クォータ
ユーザーがAPIを呼び出しできる「最大回数」を設定できます APIの呼び出し回数がリミットに達すると、「ピリオド」の期間中、APIの呼び出しが失敗し 401(Unauthorized) が返されるようになります
設定できるパラメーターは以下の通りです
名前 | 説明 |
---|---|
Limit | 呼び出しを行える最大回数です。API利用者はこれを超えて「Period」期間内にAPIの呼び出しを行うことができません。 |
Offset | API Keyの利用開始時点の初期値を設定します。月や週の途中から契約が始まるようなケースで呼び出し回数を調整するために利用できます |
Period | リミットが適用される期間です。執筆時点ではDAY , WEEK , MONTH が指定できます |
スロットリング
ユーザーがAPIを呼び出しできる「流量」を設定できます
1秒間あたりのAPIの呼び出しが、設定された「レート」を超えないように調整されます
「レート」を超えたAPI呼び出しは失敗し429(Too Many Requests)が返されます
ただし「レート」を超えたAPI呼び出しが直ちに429になるわけでなく、「バースト」に設定された値分バースト(レートを超えた呼び出し)が許容されます
詳しい情報については以下をご参考ください
API リクエストを調整してスループットを向上させる - Amazon API Gateway
設定できるパラメーターは以下の通りです
名前 | 説明 |
---|---|
RateLimit | 1秒あたりのリクエスト数の平均 |
BurstLimit | バーストとして許容されるリクエスト数 |
設定
今回は Severless Framework(以下 sls) を利用して設定していきます
設定する使用量プランは以下です
プラン | リミット | ピリオド | レート | バースト |
---|---|---|---|---|
free | 100 | DAY | 1 | 2 |
paid | 200 | DAY | 2 | 4 |
これを sls で設定するにはserveless.yml に以下のように設定します
provider: name: aws runtime: python3.8 apiKeys: - free: # 使用量プラン - free-plan-key # 名前だけを指定するとKeyが自動生成されdeploy時に標準出力されます - name: free-plan-key2 # 名前とKeyの値を指定することもできます value: a123456789012345678901234567890 # 30-128文字の英数字 - paid: - paid-plan-key usagePlan: - free: quota: limit: 100 period: DAY throttle: burstLimit: 2 rateLimit: 1 - paid: quota: limit: 200 period: DAY throttle: burstLimit: 4 rateLimit: 2 functions: api: handler: handler.sample_api events: - http: path: sample method: get private: true # 使用量プラン有効にするエンドポイントにtrueを指定します
※ CFnを使用している関係でAPI Keyを更新するのにAPI Key名を変更する必要がある点に注意してください
確認
お手軽にベンチマークをかけれる vegeta を使って1秒間で2リクエストを5秒間アクセスし続けて確認してみます
# ※API_KEYにfreeのAPI Keyが設定されてる前提です echo "GET https://sample-api-gw.example.com" | vegeta attack -header "X-API-KEY: $API_KEY" -rate=2 -duration=5s | vegeta report
Requests [total, rate, throughput] 10, 2.22, 1.31 Duration [total, attack, wait] 4.5744649s, 4.500001989s, 74.462911ms Latencies [mean, 50, 95, 99, max] 78.439416ms, 68.357618ms, 214.098603ms, 214.098603ms, 214.098603ms Bytes In [total, mean] 8788, 878.80 Bytes Out [total, mean] 0, 0.00 Success [ratio] 60.00% Status Codes [code:count] 200:6 429:4
何回か試したところばらつきがありましたが、いくつかのリクエストが429が返却され失敗していることが確認できました
# ※API_KEYにpaidのAPI Keyが設定されてる前提です echo "GET https://sample-api-gw.example.com" | vegeta attack -header "X-API-KEY: $API_KEY" -rate=2 -duration=5s | vegeta report
Requests [total, rate, throughput] 10, 2.22, 2.17 Duration [total, attack, wait] 4.60279174s, 4.499963195s, 102.828545ms Latencies [mean, 50, 95, 99, max] 88.608431ms, 78.065841ms, 214.345343ms, 214.345343ms, 214.345343ms Bytes In [total, mean] 14564, 1456.40 Bytes Out [total, mean] 0, 0.00 Success [ratio] 100.00% Status Codes [code:count] 200:10
API Keyを paid に変更したところ、429が返却されなくりました
今度は1秒間で4リクエストを5秒間アクセスし続けます
# ※API_KEYにpaidのAPI Keyが設定されてる前提です echo "GET https://sample-api-gw.example.com" | vegeta attack -header "X-API-KEY: $API_KEY" -rate=4 -duration=5s | vegeta report
Requests [total, rate, throughput] 20, 4.21, 3.96 Duration [total, attack, wait] 4.802387722s, 4.745446072s, 56.94165ms Latencies [mean, 50, 95, 99, max] 74.479068ms, 70.526317ms, 150.019519ms, 211.864973ms, 211.864973ms Bytes In [total, mean] 27693, 1384.65 Bytes Out [total, mean] 0, 0.00 Success [ratio] 95.00% Status Codes [code:count] 200:19 429:1
こちらもばらつきがありましたが、429が返されるようになりました
プログラムから操作する
boto3をつかってpythonで使用量プラン、API Keyを操作する方法をご紹介します
使用量を取得
import boto3 apigw = boto3.client("apigateway") res = apigw.get_usage( usagePlanId="${使用量プランID}", startDate="2019-12-01", endDate="2019-12-16", ) print(res["items"])
{'${プランID}': [[0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [0, 1000], [215, 785]]}
新たなAPI Keyを作成し、使用量プランに紐付ける
import boto3 import uuid apikey = apigw.create_api_key( name="${API Key名}", value=str("API Key"), ) res = apigw.create_usage_plan_key( usagePlanId="${使用量プランID}", keyId=apikey["id"], keyType="API_KEY", )
最後に
APIの使用量など永続可するのも難しく、課金に直結し失敗が許されない機能となりがちで、実装するのが難しい機能と思われます。 この部分にAWSで使われている仕組みを使えるのは助かる場面は多いのではないでしょうか
補足
スロットリングの制御に使われているトークンバケットについて補足しておきます トークンバケットは、ネットワークの流量などを調整するアルゴリズムで単純にレートを超える流量を禁止するのではなく、ある程度のバーストを許容するのが特徴です。詳しくは以下をご参考ください トークンバケット - Wikipedia
使用量プランのスロットリング機能は以下のAPI Gatewayのスロットリング機能を個別に適用できるようにしているものと予想されます API リクエストを調整してスループットを向上させる - Amazon API Gateway
そのため RateLimit と BurstLimit の以下の図のように BurstLimit の設定された値分 RateLimit の余剰分が溜まっていき、その分バーストのリクエストが許されるものと思われます